/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package tetris.model;

import java.awt.Graphics;
import java.util.ArrayList;
import tetris.model.events.FigureActionEvent;
import tetris.model.events.FigureActionListener;
import tetris.model.navigation.Direction;
import tetris.model.navigation.Position;

/**
 * Класс фигуры.
 * @author vadim
 */
public class Figure {

    /**
     * Форма фигуры.
     */
    protected ArrayList<boolean[]> _form;    

    /**
     * Позиция фигуры в стакане.
     */
    protected Position _position;

    /**
     * Стакан, которому принадлежит фигура.
     */
    protected Glass _glass;

    /**
     * Массив слущателей реагирующих на действия фигуры.
     */
    protected ArrayList<FigureActionListener> _listeners;

    /**
     * Конструктор.
     */
    public Figure() {
        _listeners = new ArrayList<FigureActionListener>();
        _form = new ArrayList<boolean[]>();
    }

    /**
     * Задание позиции фигуры в стакане.
     * @param glass стакан.
     * @param position позиция.
     * @return удалось ли задать позицию.
     */
    public boolean setPosition(Glass glass, Position position) {
        if (glass != null && position != null && position.isValid()) {
            _glass = glass;
            _position = position;
            return true;
        }
        return false;    
    }
    
    
    private void rotate() {
        boolean[] row = {false,false,false,false,false};
        ArrayList<boolean[]> form = new ArrayList<boolean[]>();
        for (int i = 0; i < _form.size(); i++) {
            form.add(row.clone());
        }
        
        for (int i = 0; i < _form.size(); i++) {
            for(int j = 0; j < _form.get(i).length; j++) {
                form.get(j)[form.size()-1-i] = _form.get(i)[j];
            }
        }
        _form = form;
        for (FigureActionListener listener : _listeners) {
            listener.figureAreTurned(null);
        }
    }

    /**
     * Функция движения фигуры.
     * @param direct направление движения.
     * @return удалось ли двинуться.
     */
    public boolean move(Direction direct) {
        if (this.moveIsPossible(direct)) {
            _position = _position.next(direct);
            for (FigureActionListener listener : _listeners) {
                listener.figureMakedMove(null);
            }
            return true;
        } else if (direct.hashCode() == Direction.down().hashCode()) {
            for (FigureActionListener listener : _listeners) {
                listener.figureAreFallen(new FigureActionEvent(this));
            }
        }
        return false;   
    }
    
    /**
     * Функция возвращает форму фигуры.
     * @return форма.
     */
    public ArrayList<boolean[]> form() {
        return _form;
    }

    /**
     * Функция проверяющая форможность движения фигуры в заданном направлении.
     * @param direct направление.
     * @return результат проверки.
     */
    public boolean moveIsPossible(Direction direct) {
        boolean result = _position.hasNext(direct) && _glass != null;
        if (result) {
            Position pos = _position.next(direct);
            Figure clone = (Figure)this.clone();
            clone._position = pos;
            result = !_glass.crossing(clone); 
            ArrayList<Figure> array;
            array = (ArrayList<Figure>)_glass.secondFigures().clone();
            array.add(_glass.currentFigure());
            if (result) {
                int width = _form.get(0).length; 
                for(int i = 0; i < array.size(); i++) {
                    if(this != array.get(i)) {
                        Figure current = array.get(i); 
                        int widthTemp = current.form().get(0).length;
                        int modificatorHeight = _form.size() / 2;
                        for (int z = 0; z < _form.size(); z++) {
                            int modificatorWidth = -width / 2;
                            for (int y = 0; y < _form.get(z).length; y++) {
                                int modificatorHeightC = current.form().size() / 2;
                                for (int j = 0 ; j < current.form().size(); j++) {
                                    int modificatorWidthC = -widthTemp / 2;
                                    for ( int k = 0; k < current.form().get(j).length; k++) {
                                        if (_form.get(z)[y] && current.form().get(j)[k] && 
                                               clone.position().row() + modificatorHeight == 
                                                current.position().row() + modificatorHeightC && 
                                                clone.position().column() + modificatorWidth == 
                                                current.position().column() + modificatorWidthC) {
                                            result = false;
                                        }
                                        modificatorWidthC++;
                                    }
                                    modificatorHeightC--;
                                }
                                modificatorWidth++;
                            }
                            modificatorHeight--;
                        }
                    }
                }
            }
        }
        return result;
    }

    Position position() {
        return _position;
    }
    
    /**
     * Функция добавляющаю фигуре слушателя.
     * @param listener слушатель.
     */
    public void addListener(FigureActionListener listener) {
        _listeners.add(listener);
    }
    
    
    /**
     * Функция поворота фигуры.
     * @return удалось ли повернуть.
     */
    public boolean turn() {
        if(turnIsPossible()) {
            rotate();
            return true;
        }
        return false;
    }
    
    /**
     * Функция отрисовки фигуры.
     * @param g графика.
     * @return результат.
     */
    public boolean draw(Graphics g) {
        int modificatorHeight = -_form.size() / 2;
        for (boolean[] array : _form) {
            int modificatorWidth = -array.length / 2;
            for (boolean cell : array) {
                if (cell == true) {
                    g.fillRect((_glass.glassShiftWidth() + _position.column()
                            + modificatorWidth) * 30 + 1, (_glass.glassShiftHeight()
                            + _glass.height() - _position.row() + modificatorHeight) * 30 + 1,
                            30, 30);
                }
                modificatorWidth++;
            }
            modificatorHeight++;
        }
        return true;
    }

    /**
     * Функция клонирования фигуры.
     * @return клон фигуры.
     */
    @Override
    public Object clone() {
        Figure clone = new Figure();
        clone._form.clear();
        for(int i = 0; i < _form.size(); i++) {
            clone._form.add(this._form.get(i).clone());
        }
        clone._position = _position;
        clone._glass = _glass;
        return clone;
        
    }
    
    void setForm(ArrayList<boolean[]> form) {
        _form = form;
    }

    
    /**
     * Функция очистки массива слушателей фигуры.
     */
    public void clearListeners() {
        _listeners.clear();
    }
    
    /**
     * Функция проверки возможности поворота фигуры.
     * @return результат.
     */
    public boolean turnIsPossible() {
        boolean result = true;
        Figure clone = (Figure)this.clone();
        clone.rotate();
        result = !_glass.crossing(clone);
        if (result) {
            int maxHeight, maxHeightC;
            int maxWidth, maxWidthC;
            int minHeight, minHeightC;
            int minWidth, minWidthC;
            maxWidth = 0;
            maxHeight = 0;
            minHeight = _form.size();
            minWidth = _form.get(0).length;
            for (int i = 0; i < _form.size(); i++) {
                for (int j = 0; j < _form.get(i).length; j++) {
                    if (_form.get(i)[j]) {
                        if (j > maxWidth) {
                            maxWidth = j;
                        }
                        if (j < minWidth) {
                            minWidth = j;
                        }
                        if (i > maxHeight) {
                            maxHeight = i;
                        }
                        if (i < minHeight) {
                            minHeight = i;
                        }
                    }
                }
            }
            
            maxWidthC = 0;
            maxHeightC = 0;
            minHeightC = clone.form().size();
            minWidthC = clone.form().get(0).length;
            for (int i = 0; i < clone.form().size(); i++) {
                for (int j = 0; j < clone.form().get(i).length; j++) {
                    if (clone.form().get(i)[j]) {
                        if (j > maxWidthC) {
                            maxWidthC = j;
                        }
                        if (j < minWidthC) {
                            minWidthC = j;
                        }
                        if (i > maxHeightC) {
                            maxHeightC = i;
                        }
                        if (i < minHeightC) {
                            minHeightC = i;
                        }
                    }
                }
            }
            if (minHeightC < minHeight) {
                minHeight = minHeightC;
            }
            if (maxHeightC > maxHeight) {
                maxHeight = maxHeightC;
            }
            if (minWidthC < minWidth) {
                minWidth = minWidthC;
            }
            if (maxWidthC > maxWidth) {
                maxWidth = maxWidthC;
            }
            
            for (int i = minHeight; i < maxHeight + 1; i++) {
                for (int j = minWidth; j < maxWidth + 1; j++) {
                   if (_glass.notEndedLines().form().get(_position.row() + i - 
                           _form.size()/2)[_position.column() + j - _form.get(i).length/2]) {
                       result = false;
                   } 
                }
            }
            ArrayList<Figure> array;
            array = (ArrayList<Figure>)_glass.secondFigures().clone();
            array.add(_glass.currentFigure());
            if (result) {
                int width = _form.get(0).length; 
                for(int i = 0; i < array.size(); i++) {
                    if(this != array.get(i)) {
                        Figure current = array.get(i); 
                        int widthTemp = current.form().get(0).length;
                        for (int z = 0; z < _form.size(); z++) {
                            for (int y = 0; y < _form.get(z).length; y++) {
                                int modificatorHeightC = current.form().size() / 2;
                                for (int j = 0 ; j < current.form().size(); j++) {
                                    int modificatorWidthC = -widthTemp / 2;
                                    for ( int k = 0; k < current.form().get(j).length; k++) {
                                        if (current.form().get(j)[k] && 
                                                current.position().row() + modificatorHeightC >=
                                                _position.row() + minHeight - _form.size()/2 &&
                                                current.position().row() + modificatorHeightC <=
                                                _position.row() + maxHeight - _form.size()/2 && 
                                                current.position().column() + modificatorWidthC >=
                                                _position.column() - _form.get(0).length/2 + 
                                                minWidth && current.position().column() + 
                                                modificatorWidthC <= _position.column() - 
                                                _form.get(0).length/2 + maxWidth) {
                                            result = false;
                                        }
                                        modificatorWidthC++;
                                    }
                                    modificatorHeightC--;
                                }
                            }
                        }
                    }
                }
            }
        }
        return result;
    }
}
